﻿using Mg.Domain;
using Mg.Reflection;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Data;
using System.Linq.Expressions;

namespace Mg.ExpressionParser
{
    public class SqlParserCore<T> where T : IEntity
    {
        private readonly SqlPack _sqlPack;
        private readonly Type _type = typeof(T);
        public string SqlStr
        {
            get
            {
                return _sqlPack.ToString();
            }
        }

        public SqlParserCore(IDbSqlParser parser)
        {
            _sqlPack = new SqlPack(parser);
        }

        public Dictionary<string, object> DbParams
        {
            get { return _sqlPack.DbParams; }
        }

        public List<IDbDataParameter> DbDataParams
        {
            get { return _sqlPack.SqlParameters; }
        }

        public void Clear()
        {
            _sqlPack.Clear();
        }

        private string SelectParser(params Type[] ary)
        {
            _sqlPack.Clear();
            _sqlPack.IsSingleTable = false;
            foreach (var item in ary)
            {
                _sqlPack.SetTableAlias(item.Name);
            }
            return "SELECT {0} \nFROM " + _type.Name + " " + _sqlPack.GetTableAlias(_type.Name);
        }

        public string[] SelectFieldNames
        {
            get
            {
                return _sqlPack.SelectFields.Select(c => c.Split('.')[1]).ToArray();
            }
        }

        public SqlParserCore<T> Select(Expression<Func<T, object>> expression = null)
        {
            string sql = SelectParser(_type);
            if (expression == null)
            {
                string[] fields = EntityPropertyInfoCollection.GetPropertyInfos(_type).Select(c => c.Name).ToArray();
                string alias = _sqlPack.GetTableAlias(_type.Name);
                _sqlPack.SelectFields.AddRange(fields.Select(c => alias + "." + c));
                _sqlPack.Sql.AppendFormat(sql, _sqlPack.SelectFieldStr);
            }
            else
            {
                ExpressionParserProvider.Select(expression.Body, _sqlPack);
                _sqlPack.Sql.AppendFormat(sql, _sqlPack.SelectFieldStr);
            }
            return this;
        }

        public SqlParserCore<T> Select<T2>(Expression<Func<T, T2, object>> expression = null)
        {
            Type t2 = typeof(T2);
            string sql = SelectParser(_type, t2);
            if (expression == null)
            {
                string[] fields = EntityPropertyInfoCollection.GetPropertyInfos(t2).Select(c => c.Name).ToArray();
                string alias = _sqlPack.GetTableAlias(_type.Name);
                _sqlPack.SelectFields.AddRange(fields.Select(c => alias + "." + c));
                _sqlPack.Sql.AppendFormat(sql, _sqlPack.SelectFieldStr);
            }
            else
            {
                ExpressionParserProvider.Select(expression.Body, _sqlPack);
                _sqlPack.Sql.AppendFormat(sql, _sqlPack.SelectFieldStr);
            }
            return this;
        }

        private SqlParserCore<T> JoinParser<T2>(Expression<Func<T, T2, bool>> expression, string leftOrRightJoin = "")
        {
            string joinTableName = typeof(T2).Name;
            _sqlPack.SetTableAlias(joinTableName);
            _sqlPack.Sql.AppendFormat(" \n{0}JOIN {1} ON", leftOrRightJoin, joinTableName + " " + _sqlPack.GetTableAlias(joinTableName));
            ExpressionParserProvider.Join(expression.Body, _sqlPack);
            return this;
        }
        private SqlParserCore<T> JoinParser2<T2, T3>(Expression<Func<T2, T3, bool>> expression, string leftOrRightJoin = "")
        {
            string joinTableName = typeof(T3).Name;
            _sqlPack.SetTableAlias(joinTableName);
            _sqlPack.Sql.AppendFormat(" \n{0}JOIN {1} ON", leftOrRightJoin, joinTableName + " " + _sqlPack.GetTableAlias(joinTableName));
            ExpressionParserProvider.Join(expression.Body, _sqlPack);
            return this;
        }


        public SqlParserCore<T> Join<T2>(Expression<Func<T, T2, bool>> expression)
        {
            return JoinParser(expression);
        }
        public SqlParserCore<T> Join<T2, T3>(Expression<Func<T2, T3, bool>> expression)
        {
            return JoinParser2(expression);
        }

        public SqlParserCore<T> InnerJoin<T2>(Expression<Func<T, T2, bool>> expression)
        {
            return JoinParser(expression, "INNER ");
        }
        public SqlParserCore<T> InnerJoin<T2, T3>(Expression<Func<T2, T3, bool>> expression)
        {
            return JoinParser2(expression, "INNER ");
        }

        public SqlParserCore<T> LeftJoin<T2>(Expression<Func<T, T2, bool>> expression)
        {
            return JoinParser(expression, "LEFT ");
        }
        public SqlParserCore<T> LeftJoin<T2, T3>(Expression<Func<T2, T3, bool>> expression)
        {
            return JoinParser2(expression, "LEFT ");
        }

        public SqlParserCore<T> RightJoin<T2>(Expression<Func<T, T2, bool>> expression)
        {
            return JoinParser(expression, "RIGHT ");
        }
        public SqlParserCore<T> RightJoin<T2, T3>(Expression<Func<T2, T3, bool>> expression)
        {
            return JoinParser2(expression, "RIGHT ");
        }

        public SqlParserCore<T> FullJoin<T2>(Expression<Func<T, T2, bool>> expression)
        {
            return JoinParser(expression, "FULL ");
        }
        public SqlParserCore<T> FullJoin<T2, T3>(Expression<Func<T2, T3, bool>> expression)
        {
            return JoinParser2(expression, "FULL ");
        }

        public SqlParserCore<T> Where(Expression<Func<T, bool>> expression)
        {
            _sqlPack.Sql.Append(" \nWHERE");
            ExpressionParserProvider.Where(expression.Body, _sqlPack);
            return this;
        }

        public SqlParserCore<T> GroupBy(Expression<Func<T, object>> expression)
        {
            _sqlPack.Sql.Append(" \nGROUP BY ");
            ExpressionParserProvider.GroupBy(expression.Body, _sqlPack);
            return this;
        }

        public SqlParserCore<T> OrderBy(Expression<Func<T, object>> expression)
        {
            _sqlPack.Sql.Append(" \nORDER BY ");
            ExpressionParserProvider.OrderBy(expression.Body, _sqlPack);
            return this;
        }

        public SqlParserCore<T> Max(Expression<Func<T, object>> expression)
        {
            _sqlPack.Clear();
            ExpressionParserProvider.Max(expression.Body, _sqlPack);
            return this;
        }

        public SqlParserCore<T> Min(Expression<Func<T, object>> expression)
        {
            _sqlPack.Clear();
            ExpressionParserProvider.Min(expression.Body, _sqlPack);
            return this;
        }

        public SqlParserCore<T> Avg(Expression<Func<T, object>> expression)
        {
            _sqlPack.Clear();
            ExpressionParserProvider.Avg(expression.Body, _sqlPack);
            return this;
        }

        public SqlParserCore<T> Count(Expression<Func<T, object>> expression = null)
        {
            _sqlPack.Clear();
            if (expression == null)
            {
                string tableName = _type.Name;

                _sqlPack.SetTableAlias(tableName);
                string tableAlias = _sqlPack.GetTableAlias(tableName);

                if (!string.IsNullOrWhiteSpace(tableAlias))
                {
                    tableName += " " + tableAlias;
                }
                _sqlPack.Sql.AppendFormat("SELECT COUNT(1) FROM {0}", tableName);
            }
            else
            {
                ExpressionParserProvider.Count(expression.Body, _sqlPack);
            }

            return this;
        }

        public SqlParserCore<T> Sum(Expression<Func<T, object>> expression)
        {
            _sqlPack.Clear();
            ExpressionParserProvider.Sum(expression.Body, _sqlPack);
            return this;
        }

        public SqlParserCore<T> Delete()
        {
            _sqlPack.Clear();
            _sqlPack.IsSingleTable = true;
            _sqlPack.SetTableAlias(_type.Name);
            _sqlPack.Sql.Append("DELETE " + _type.Name);
            return this;
        }

        public SqlParserCore<T> Update(Expression<Func<object>> expression = null)
        {
            _sqlPack.Clear();
            _sqlPack.IsSingleTable = true;
            _sqlPack.Sql.Append("UPDATE " + _type.Name + " SET ");
            ExpressionParserProvider.Update(expression.Body, _sqlPack);
            return this;
        }
    }
}
